/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package SessionBeans;

import SessionBeans.Sharing.RightsService;
import SessionBeans.Sharing.SharingService;
import SessionBeans.actions.*;
import api.Beans.UserLogin;
import api.Rights.RightsEnum;
import api.SessionBeans.TCloudServiceRemote;
import api.action.ActionInterface;
import api.action.ActionPackageClient;
import api.action.ActionPackageServer;
import api.action.actionImplementations.client.*;
import api.action.crates.Client.DeleteFileFolderCrate;
import api.action.crates.Client.NewUpdateFileCrate;
import api.action.crates.Client.NewUpdateFolderCrate;
import api.action.crates.Server.*;
import api.exception.TCloudAplicationException;
import api.localModel.LocalFileStatus;
import entities.*;
import java.util.*;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;

/**
 *
 * @author Tomas
 */
@Stateless(mappedName = "Service")
public class TCloudService implements TCloudServiceRemote {

    @EJB
    private RightsService rightsService;
    @EJB
    private SharingService sharingService;
    @EJB
    private UserSessionBean userSessionBean;
    @EJB
    private DeleteFolderService deleteFolderService;
    @EJB
    private DeleteFileService deleteFileService;
    @EJB
    private UpdateFolderService updateFolderService;
    @EJB
    private UpdateFileService updateFileService;
    @EJB
    private NewFolderService newFolderService;
    @EJB
    private NewFileService newFileService;
    @PersistenceContext(unitName = "tCloudEE-ejbPU")
    private EntityManager em;

    @Override
    public void uploadChanges(ActionPackageServer actionPackage) throws TCloudAplicationException {

        Long userId = actionPackage.getUserId();
        User user = em.find(User.class, userId);
        int accountHeaderVersion = actionPackage.getAccountHeaderVersion();
        AccountHeader accHeader = user.getAccountHeaderList().get(0);
        List<ActionInterface> actions = actionPackage.getActions();
//        InvokeActionInterface lookup = (InvokeActionInterface)new InitialContext().llookup("NEW_FILE");


        for (ActionInterface actionInterface : actions) {
//          lookup.processAction(actionInterface.getActionData(), userId, accHeader);
            switch (actionInterface.getActionType()) {
                case NEW_FILE:
                    newFileService.processAction((NewFileCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
                case NEW_FOLDER:
                    newFolderService.processAction((NewFolderCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
                case UPDATE_FILE:
                    updateFileService.processAction((UpdateFileCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
                case UPDATE_FOLDER:
                    updateFolderService.processAction((UpdateFolderCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
                case DELETE_FILE:
                    deleteFileService.processAction((DeleteFileCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
                case DELETE_FOLDER:
                    deleteFolderService.processAction((DeleteFolderCrate) actionInterface.getActionData(), userId, accHeader);
                    break;
            }
        }

        accHeader.setVersion(accHeader.getVersion() + 1);
        em.persist(accHeader);

        try {
            em.flush();
        } catch (PersistenceException e) {
            throw new TCloudAplicationException(50, e);
        }
    }

    // Add business logic below. (Right-click in editor and choose
    // "Insert Code > Add Business Method") 
    @Override
    public boolean isUpdate(long userId, int accHeaderVersion) {
        User find = em.find(User.class, userId);
        AccountHeader accHeader = find.getAccountHeaderList().get(0);
        if (accHeader.getVersion() > accHeaderVersion) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public ActionPackageClient downloadUpdateActions(LocalFileStatus localStatus) {
        User user = em.find(User.class, localStatus.getUserId());
        boolean generateShared = user.getAccountHeaderList().get(0).isDownloadShared();
        Folder rootFolder = user.getAccountHeaderList().get(0).getRootFolder();

        api.localModel.File remoteRootFile = localStatus.getRootFile();
        api.localModel.File removeSharedFolder = removeSharedFolder(remoteRootFile);

        List<ActionInterface> generateActions = generateActions(rootFolder, remoteRootFile, RightsEnum.DELETE);

        if (generateShared) {
//            generateActions.addAll(generateSharedActions(user, removeSharedFolder));
        }

        return new ActionPackageClient(generateActions, user.getAccountHeaderList().get(0).getVersion());
    }

    private api.localModel.File removeSharedFolder(api.localModel.File remoteRootFile) {
        Iterator<api.localModel.File> iterator = remoteRootFile.getChilds().iterator();
        while (iterator.hasNext()) {
            api.localModel.File file = iterator.next();
            if (file.getName().equalsIgnoreCase("shared")) {
                iterator.remove();
                return file;
            }
        }
        return null;
    }

    private List<ActionInterface> generateActions(Folder rootFolder, api.localModel.File remoteRootFile, RightsEnum rights) {
        List<ActionInterface> actions = new ArrayList<>();
        generateActionsRe(remoteRootFile, rootFolder, actions, rights);

        return actions;
    }

    private void generateActionsRe(api.localModel.File remoteRootFile, Folder rootFolder, List<ActionInterface> actions, RightsEnum rights) {
        List<api.localModel.File> remoteFileList = new ArrayList<>();
        if (remoteRootFile != null) {
            remoteFileList.addAll(remoteRootFile.getChilds());
        }
        List<Folder> localFolderList = new ArrayList<>();
        localFolderList.addAll(rootFolder.getFolderList());
        List<entities.File> localFileList = new ArrayList<>();
        localFileList.addAll(rootFolder.getFilesList());

        for (Iterator<api.localModel.File> itRemote = remoteFileList.iterator(); itRemote.hasNext();) {
            boolean find = false;
            api.localModel.File remoteFile = itRemote.next();

            for (Iterator<entities.File> itLocalFiles = localFileList.iterator(); itLocalFiles.hasNext();) {
                entities.File localFile = itLocalFiles.next();
                if (remoteFile.getName().equalsIgnoreCase(localFile.getFileName())) {
                    find = true;
                    generateAction(remoteFile, localFile, actions, rights);
                    itRemote.remove();
                    itLocalFiles.remove();
                    break;
                }
            }

            if (!find) {
                for (Iterator<Folder> itLocalFolders = localFolderList.iterator(); itLocalFolders.hasNext();) {
                    Folder localFolder = itLocalFolders.next();
                    if (remoteFile.getName().equalsIgnoreCase(localFolder.getName())) {
                        generateAction(remoteFile, localFolder, actions, rights);
                        itRemote.remove();
                        itLocalFolders.remove();
                        generateActionsRe(remoteFile, localFolder, actions, rights);
                        break;
                    }
                }
            }

        }

        for (api.localModel.File file : remoteFileList) {
            String[] path = generatePath(rootFolder);
            path = Arrays.copyOf(path, path.length + 1);
            path[path.length - 1] = file.getName();
            actions.add(new DeleteFileFolderAction(new DeleteFileFolderCrate(path, 0)));
        }

        for (File file : localFileList) {
            FileBlob find = em.find(FileBlob.class, file.getFileBlobId());
            actions.add(new NewFileAction(new NewUpdateFileCrate(generatePath(file), file.getVersion(), RightsEnum.DELETE, find.getValue())));
        }
 
        for (Folder folder : localFolderList) {
            actions.add(new NewFolderAction(new NewUpdateFolderCrate(generatePath(folder), RightsEnum.DELETE, folder.getVersion())));
            generateActionsRe(null, folder, actions, rights);
        }

    }

    private void generateAction(api.localModel.File remoteFile, entities.File localFile, List<ActionInterface> actions, RightsEnum rights) {
        if (localFile.getVersion() > remoteFile.getVersion()) {
            FileBlob find = em.find(FileBlob.class, localFile.getFileBlobId());
            actions.add(new UpdateFileAction(new NewUpdateFileCrate(generatePath(localFile), localFile.getVersion(), rights, find.getValue())));
        }
    }

    private void generateAction(api.localModel.File remoteFile, entities.Folder localFolder, List<ActionInterface> actions, RightsEnum rights) {
        if (remoteFile.getVersion() > localFolder.getVersion()) {
            actions.add(new UpdateFolderAction(new NewUpdateFolderCrate(generatePath(localFolder), rights, localFolder.getVersion())));
        }
    }

    private String[] generatePath(entities.File file) {
        String[] path = generatePath(file.getFolderId());

        path = Arrays.copyOf(path, path.length + 1);
        path[path.length - 1] = file.getFileName();

        return path;
    }

    private String[] generatePath(entities.Folder folder) {
        String path = "";
        Folder tempFolder = folder;
        if (tempFolder.getParentFolder() == null) {
            return new String[0];
        }
        for (;;) {
            path = tempFolder.getName() + "-" + path;
            if (tempFolder.getParentFolder() == null || tempFolder.getParentFolder().getParentFolder() == null) {
                break;
            }
            tempFolder = tempFolder.getParentFolder();
        }
        return path.split("-");
    }

    @Override
    public UserLogin loginUser(String userName, String password) {
        if (userSessionBean.tryLogin(userName, password)) {
            User login = userSessionBean.login(userName, password);
            return new UserLogin(login.getName(), login.getId());
        } else {
            return null;
        }
    }
    
}
